﻿// -----------------------------------------------------------------------
// <copyright company="Christoph van der Fecht - VDsoft">
// This code can be used in commercial, free and open source projects.
// </copyright>
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;
using VDsoft.SeriesNewsReader.Model;
using Newtonsoft.Json;

namespace VDsoft.SeriesNewsReader.Repository
{
	public interface ISeriesRepository
	{
		/// <summary>
		/// Applys the filter criteria on the collection of series.
		/// </summary>
		/// <param name="filterCriteria">Criteria to filter.</param>
		void ApplyFilter(string filterCriteria);

		/// <summary>
		/// Applys the filter with all provided criterias.
		/// </summary>
		/// <param name="criteria">All criterias to filter.</param>
		void ApplyFilter(SearchCriteria criteria);

		/// <summary>
		/// Applys the language filter on the collection of series.
		/// </summary>
		/// <param name="language">Language to apply.</param>
		void ApplyLanguageFilter(string language);

		/// <summary>
		/// Applys the resolution filter on the collection of series.
		/// </summary>
		/// <param name="res">Requested resolution.</param>
		void ApplyReslutionFilter(Resolution res);

		/// <summary>
		/// Sends the filtered result.
		/// </summary>
		/// <param name="series">Filter result to send.</param>
		void SendFilterResult(IEnumerable<RssFeed> series);
	}

	/// <summary>
	/// Repository to handl all access to the series.
	/// </summary>
	public class SeriesRepository : ISeriesRepository
	{
		/// <summary>
		/// Default instance of the <see cref="SeriesRepository"/> class.
		/// </summary>
		private static readonly SeriesRepository DefaultInstance = new SeriesRepository();

		/// <summary>
		/// Collection of all received series.
		/// </summary>
		private IEnumerable<RssFeed> allSeries = null;

		/// <summary>
		/// Prevents a default instance of the <see cref="SeriesRepository"/> class from being created.
		/// </summary>
		private SeriesRepository()
		{
		}

		/// <summary>
		/// Gets the default instance of the <see cref="SeriesRepository"/> class.
		/// </summary>
		public static SeriesRepository Instance
		{
			get
			{
				return DefaultInstance;
			}
		}

		/// <summary>
		/// Applys the filter criteria on the collection of series.
		/// </summary>
		/// <param name="filterCriteria">Criteria to filter.</param>
		public void ApplyFilter(string filterCriteria)
		{
			if (string.IsNullOrEmpty(filterCriteria))
			{
				return;
			}

			if (filterCriteria.Equals("AllSeries"))
			{
				this.SendFilterResult(this.allSeries);
				return;
			}

			if (filterCriteria.Equals("AllMySeries"))
			{
				this.SendFilterResult(this.BuildAllMySeriesCollection());
				return;
			}

			IEnumerable<RssFeed> collection = this.allSeries;

			collection = collection.Where(x => x.Title.Contains(filterCriteria)).OrderBy(x => x.Title);
			this.SendFilterResult(collection);
		}

		/// <summary>
		/// Applys the filter with all provided criterias.
		/// </summary>
		/// <param name="criteria">All criterias to filter.</param>
		public void ApplyFilter(SearchCriteria criteria)
		{
			IEnumerable<RssFeed> collection = null;

			if (!string.IsNullOrEmpty(criteria.SearchString))
			{
				if (criteria.SearchString.Equals("All My Series"))
				{
					collection = this.BuildAllMySeriesCollection();
				}
				else if (criteria.SearchString.Equals("All Series"))
				{
					collection = this.allSeries;
				}
				else
				{
					collection = this.allSeries.Where(x => x.Title.StartsWith(criteria.SearchString));
				}
			}

			// filter language
			collection = this.FilterLanguage(criteria.Language, collection);

			// filter resolution
			collection = this.FilterResolution(criteria.Resolution, collection);

			this.SendFilterResult(collection);
		}

		/// <summary>
		/// Filters the series for resolution.
		/// </summary>
		/// <param name="resolution">Resolution to filter.</param>
		/// <param name="collection">Collection to filter.</param>
		/// <returns>Filtered collection.</returns>
		private IEnumerable<RssFeed> FilterResolution(Resolution resolution, IEnumerable<RssFeed> collection)
		{
			if (collection == null)
			{
				collection = this.allSeries;
			}

			switch (resolution)
			{
				case Resolution.HDReady:
					{
						collection = collection.Where(x => x.Resolution == Resolution.HDReady);
						break;
					}
				case Resolution.FullHD:
					{
						collection = collection.Where(x => x.Resolution == Resolution.FullHD);
						break;
					}
				case Resolution.HD:
					{
						collection = collection.Where(x => x.Resolution == Resolution.HDReady || x.Resolution == Resolution.FullHD);
						break;
					}
				case Resolution.All:
				case Resolution.Unknown:
				default:
					{
						break;
					}
			}

			return collection;
		}

		/// <summary>
		/// Filters the collection for language.
		/// </summary>
		/// <param name="language">Language to filter.</param>
		/// <param name="collection">Collection to filter.</param>
		/// <returns>Collection filtered by language.</returns>
		private IEnumerable<RssFeed> FilterLanguage(string language, IEnumerable<RssFeed> collection)
		{
			// filter language
			if (!string.IsNullOrEmpty(language))
			{
				if (collection != null)
				{
					collection = collection.Where(x => x.Language.Equals(language)); 
				}
				else
				{
					collection = this.allSeries.Where(x => x.Language.Equals(language));
				}
			}
			return collection;
		}

		/// <summary>
		/// Applys the language filter on the collection of series.
		/// </summary>
		/// <param name="language">Language to apply.</param>
		public void ApplyLanguageFilter(string language)
		{
			if (language.Equals("ALL"))
			{
				this.SendFilterResult(this.allSeries);
				return;
			}

			IEnumerable<RssFeed> collection  = this.allSeries;

			collection = collection.Where(x => x.Language.Equals(language)).OrderBy(x => x.Title);
			this.SendFilterResult(collection);
		}

		/// <summary>
		/// Applys the resolution filter on the collection of series.
		/// </summary>
		/// <param name="res">Requested resolution.</param>
		public void ApplyReslutionFilter(Resolution res)
		{
			IEnumerable<RssFeed> collection = this.allSeries;

			if (res == Resolution.All)
			{
				collection = collection.Where(x => x.Resolution == Resolution.FullHD || x.Resolution == Resolution.HDReady);
				this.SendFilterResult(collection);
				return;
			}

			collection = collection.Where(x => x.Resolution == res).OrderBy(x => x.Title);
			this.SendFilterResult(collection);
		}

		/// <summary>
		/// Sends the filtered result.
		/// </summary>
		/// <param name="series">Filter result to send.</param>
		public void SendFilterResult(IEnumerable<RssFeed> series)
		{
			Messenger.Default.Send<List<RssFeed>>(series.OrderBy(x => x.Title).ToList(), ViewModel.ViewModelLocator.SeriesFilterResult);
		}

		/// <summary>
		/// Initializes the repo.
		/// </summary>
		public void InitRepo()
		{
			Messenger.Default.Register<List<RssFeed>>(this, ViewModel.ViewModelLocator.SeriesToken, this.OnSeriesReceived);
		}

		/// <summary>
		/// Build a collection that contains all series the user monitores.
		/// </summary>
		/// <returns>Collection of all series the user monitores.</returns>
		private IEnumerable<RssFeed> BuildAllMySeriesCollection()
		{
			List<string> monitoredSeries = JsonConvert.DeserializeObject<List<string>>(Properties.Settings.Default.Series);
			if (monitoredSeries == null)
			{
				return null;
			}

			List<RssFeed> collection = new List<RssFeed>();
			foreach (string series in monitoredSeries)
			{
				collection.AddRange(this.allSeries.Where(x => x.Title.Contains(series)));
			}

			return collection.OrderBy(x => x.Title).AsEnumerable();
		}

		/// <summary>
		/// Stores all received series.
		/// </summary>
		/// <param name="series">Received series from the webservice.</param>
		private void OnSeriesReceived(List<RssFeed> series)
		{
			this.allSeries = series.AsEnumerable();
			this.SendFilterResult(this.allSeries);
		}
	}
}
